package com.unswift.cloud.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;
import com.unswift.cloud.annotation.LockException;
import com.unswift.cloud.annotation.LockWait;
import com.unswift.cloud.config.LockConfig;
import com.unswift.cloud.core.CommonOperator;
import com.unswift.utils.ClassUtils;
import com.unswift.utils.ExceptionUtils;
import com.unswift.utils.ObjectUtils;

@Aspect
@Component
@Api(value="数据锁AOP，分等待锁和异常锁，分别对应注解LockWait和LockException", author = "unswift", date = "2023-09-08", version = "1.0.0")
public class DataLockAop extends CommonOperator{

	@Autowired
	@ApiField("锁配置")
	private LockConfig lockConfig;
	
	@ApiMethod("等待锁的切面")
	@Pointcut("@annotation(com.unswift.cloud.annotation.LockWait)")
	public void entryLockWaitPoint() {
	}

	@ApiMethod(value="等待锁切面扩展", params = {@ApiField("切面的方法封装"), @ApiField("等待锁注解")})
	@Around("entryLockWaitPoint() && @annotation(dataLock)")
	public Object around(ProceedingJoinPoint point, LockWait dataLock) throws Throwable {
		String type=dataLock.prefix();
		ExceptionUtils.empty(type, "field.empty", "锁类型");
		String unique=getEexpressionValue(point, dataLock.unique());
		String lockKey=String.format("%s-%s", type, unique);
		logger.error("数据锁key："+lockKey);
		RLock lock=null;
		String method = ObjectUtils.initEmpty(dataLock.method(), lockConfig.getMethod());
		if("redis".equals(method)){
			lock = redisAdapter.lockWait(type, unique, lockConfig.getTimeout());//加锁
		}else if("memory".equals(method)) {
			memoryCache.lockWait(lockKey, lockConfig.getTimeout());//加锁
		}
		try {
			return point.proceed();
		} catch (Exception e) {
			throw e;
		} finally {
			if("redis".equals(method)){
				redisAdapter.unlock(lock);//解锁
			}else if("memory".equals(method)) {
				memoryCache.unlock(lockKey);//解锁
			}
		}
	}
	
	@ApiMethod("异常锁的切面")
	@Pointcut("@annotation(com.unswift.cloud.annotation.LockException)")
	public void entryLockExceptionPoint() {
	}

	@ApiMethod(value="异常锁切面扩展", params = {@ApiField("切面的方法封装"), @ApiField("异常锁注解")})
	@Around("entryLockExceptionPoint() && @annotation(lockException)")
	public Object around(ProceedingJoinPoint point, LockException lockException) throws Throwable {
		String type=lockException.prefix();
		ExceptionUtils.empty(type, "field.empty", "锁类型");
		String unique=getEexpressionValue(point, lockException.unique());
		String lockKey=String.format("%s-%s", type, unique);
		logger.error("数据锁key："+lockKey);
		RLock lock=null;
		String method = ObjectUtils.initEmpty(lockException.method(), lockConfig.getMethod());
		String[] messageArgs=lockException.messageArgs();
		Object[] args=null;
		if(ObjectUtils.isNotEmpty(messageArgs)) {
			int length=messageArgs.length;
			args=new Object[length];
			for (int i = 0; i < length; i++) {
				args[i]=getEexpressionValue(point, new String[] {messageArgs[i]});
			}
		}
		if("redis".equals(method)){
			lock = redisAdapter.lockException(type, unique, lockConfig.getTimeout(), lockException.messageCode(), args);//加锁
		}else if("memory".equals(method)) {
			boolean locked=memoryCache.lock(lockKey, lockConfig.getTimeout());//加锁
			ExceptionUtils.falseException(locked, lockException.messageCode(), args);
		}
		try {
			return point.proceed();
		} catch (Exception e) {
			throw e;
		} finally {
			if("redis".equals(method)){
				redisAdapter.unlock(lock);//解锁
			}else if("memory".equals(method)) {
				memoryCache.unlock(lockKey);//解锁
			}
		}
	}
	
	@ApiMethod(value="获取表达式的值", params = {@ApiField("切面的方法封装"), @ApiField("表达式")})
	private String getEexpressionValue(ProceedingJoinPoint point, String[] uniqueArray) {
		if(ObjectUtils.isEmpty(uniqueArray)) {
			return "";
		}
		StringBuilder lockUnique=new StringBuilder();
		Object itemLockKey;
		for (String unique : uniqueArray) {
			if(!unique.startsWith("{")) {
				lockUnique.append(unique);
			}else {
				unique=unique.substring(1, unique.length()-1);
				if(unique.equals("currUserId")) {
					lockUnique.append(this.getUserId());
				}else if(unique.startsWith("@")){
					lockUnique.append(ExceptionUtils.getMessage(unique.substring(1)));
				}else {
					Object[] args=point.getArgs();
					if(ObjectUtils.isNotEmpty(args)) {
						if(unique.equals("$")) {
							lockUnique.append(args[0].toString());
						}else if(unique.startsWith("$")) {
							if(unique.charAt(1)=='.') {
								itemLockKey = ClassUtils.getPathFieldValue(args[0], unique.substring(2));
								lockUnique.append(itemLockKey);
							}else {
								itemLockKey = ClassUtils.getPathFieldValue(args[0], unique.substring(1));
								lockUnique.append(itemLockKey);
							}
						}else if(unique.startsWith("[")) {
							int first=unique.indexOf('[');
							int end=unique.indexOf(']');
							ExceptionUtils.trueException(end==-1, "expression.error", unique, "无法找到‘]’字符");
							int index=Integer.parseInt(unique.substring(first+1, end));
							Object arrayItem;
							if(args.length>index) {
								arrayItem=args[index];
							}else {
								return "";
							}
							if(unique.length()>end+1) {
								itemLockKey = ClassUtils.getPathFieldValue(arrayItem, unique.substring(end+1));
								lockUnique.append(itemLockKey);
							}else {
								lockUnique.append(arrayItem.toString());
							}
						}else {
							throw ExceptionUtils.message("expression.error", unique, "无法识别");
						}
					}
				}
			}
			lockUnique.append("-");
		}
		if(lockUnique.length()>0) {
			lockUnique.deleteCharAt(lockUnique.length()-1);
		}
		return lockUnique.toString();
	}
}
